import {
    forwardRef,
    memo,
    useCallback,
    useContext,
    useEffect,
    useImperativeHandle, useLayoutEffect,
    useMemo,
    useReducer,
    useRef,
    useState
} from "react";
import {ThemeContext} from "./context";


function calcTotal(num) {
    console.log('calcTotal函数执行')
    let sum = 0;
    for (let i = num; i > 0; i--) {
        sum += i;
    }
    return sum;
}

function reducer(state, action) {
    switch (action.type) {
        case 'addNumber':
            return {...state, counter: state.counter + action.num}
        case 'subNumber':
            return {...state, counter: state.counter - action.num}
        default:
            return state
    }
}

const About = memo(function About(props) {
    console.log('About组件渲染')
    return (
        <div>
            <span>About组件渲染</span>
            <button onClick={props.increment}>increment(+1)</button>
        </div>
    )
})

const HelloWorld = memo(function HelloWorld(props) {
    const {info: {name, age}} = props;
    console.log('HelloWorld组件渲染')
    return (
        <div>
            <div>name:{name}-age:{age}</div>
        </div>
    )
})

const CustomInput = memo(forwardRef((props, ref) => {
    const inputRef = useRef()
    // 对ref的current进行拦截
    useImperativeHandle(ref, () => {
        return {
            focus() {
                console.log('CustomInput 输入框聚焦')
                inputRef.current.focus()
            }
        }
    })
    return (
        <input ref={inputRef}/>
    )
}))

const Star = () => {
    const [count, setCount] = useState(0)
    const [message, setMessage] = useState('hhh')
    const theme = useContext(ThemeContext);
    // 用于多个数据处理
    const [state, dispatch] = useReducer(reducer, {counter: 0, friends: []});

    const increment = () => setCount(count + 1)
    // useCallback缓存的是函数，useMemo缓存的函数返回值
    // useCallback只有返回函数传递给子组件才会对性能进行优化，目的是不希望子组件进行多次渲染，并不是为了函数进行缓存
    // useCallback可以缓存函数，依赖项数组为空，生成的函数引用(内存地址)不会变（闭包陷阱），不会使count改变，此时返回的函数不管多少次渲染拿到的同一个引用
    // 当需要将一个函数传递给子组件是，最好用useCallback进行优化，将优化之后的函数，传递给子组件（如下修改message会使Star组件重新渲染，但是使用了useCallback传递给子组件，不会使About组件重新渲染，但是修改count会），此时count每次改变返回的函数是一个新的引用
    // const incrementFn = useCallback(function increment() {
    //     console.log('useCallback');
    //     setCount(count + 1)
    // }, [count])

    // 进一步优化，count变化，要求也返回同一个函数
    // 做法一，将count移除掉，缺点：闭包陷阱，不能使得之后的count更新
    // 做法二，使用useRef，在组件多次渲染时返回的是同一个值
    const countRef = useRef();
    countRef.current = count;
    const incrementFn = useCallback(function increment() {
        console.log('useCallback');
        setCount(countRef.current + 1)
    }, [countRef])


    const result = useMemo(() => {
        console.log('memo执行')
        return calcTotal(count);
    }, [count]);

    // 多次渲染同一个info对象
    const info = useMemo(() => ({name: 'hh', age: 18}), [])

    // ref用于获取dom或者绑定值的时候解决useCallback闭包陷阱优化，绑定值的时候无论渲染多少次值不会变化
    const inputRef = useRef();
    const customInputRef = useRef();
    const handleDom = () => {
        customInputRef.current.focus();
    }


    // 渲染后再执行副作用,不会阻塞dom更新
    useEffect(() => {
        // document.title副作用
        document.title = count + '';
        console.log("设置title副作用")

        // 清除回调，当组件重新渲染或者卸载时调用执行
        return () => {
            console.log('清除设置title副作用')
        }
    }, [count]);

    useEffect(() => {
        // 事件绑定副作用调用在设置title副作用顺序之后，按着书写顺序执行的
        console.log("事件绑定副作用")

        // 清除回调，当组件重新渲染或者卸载时调用执行
        return () => {
            console.log('清除事件绑定副作用')
        }
    }, []);

    useEffect(() => {
        console.log('发出请求副作用')
        return () => {
            console.log('清除发出请求副作用')
        }
    }, [])

    // 渲染前执行副作用，阻塞dom更新
    useLayoutEffect(() => {
        console.log('useLayoutEffect')
    }, []);

    // 这种情况，使用useEffect会闪
    useLayoutEffect(() => {
        if (count === 1) {
            setCount(Math.random() + 1)
        }
    });

    console.log('Star render')

    return (
        <div>
            <span>{count}</span>
            <button onClick={() => setCount(count + 1)}>+1</button>
            <button onClick={() => setCount(count - 1)}>-1</button>
            <button onClick={() => setMessage('您好啊')}>改变message</button>
            <div style={{color: theme.color, fontSize: theme.size + 'px'}}>theme主题</div>
            <div style={{padding: '10px', border: '1px solid #000', margin: '20px 0'}}>
                {/*useReducer*/}
                <div>当前counter:{state.counter}</div>
                <button onClick={() => dispatch({type: 'addNumber', num: 3})}>counter(+3)</button>
                <button onClick={() => dispatch({type: 'subNumber', num: 3})}>counter(-3)</button>
            </div>
            <div style={{padding: '10px', border: '1px solid #000', margin: '20px 0'}}>
                <div>当前数字：{count}</div>
                <About increment={incrementFn}/>
                <div>{message}</div>
                <button onClick={() => setMessage(Math.random() + '')}>改变message</button>
            </div>
            <div style={{padding: '10px', border: '1px solid #000', margin: '20px 0'}}>
                <div>当前数字和：{result}</div>
                <button onClick={() => setCount(count + 1)}>+1</button>
                <HelloWorld info={info}/>
            </div>
            <div style={{padding: '10px', border: '1px solid #000', margin: '20px 0'}}>
                <div>useRef绑定dom</div>
                <input ref={inputRef}/>
                <button onClick={() => inputRef.current.focus()}>点击聚焦输入框</button>
            </div>
            <div style={{padding: '10px', border: '1px solid #000', margin: '20px 0'}}>
                <div>useImperativeHandle</div>
                <CustomInput ref={customInputRef}/>
                <button onClick={handleDom}>对dom进行操作</button>
            </div>
        </div>
    )
}

export default Star